<?php

/**
 * An abstract implimentation of Log_XML with DOM functionality.
 *
 * PHP version 5.
 *
 * @category Logging
 * @package  Log
 * @author   Michael Peter 'Vladimyr' Gower <designatevoid@gmail.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
 * @link     http://pear.php.net/pepr/pepr-proposal-show.php?id=487
 */

/**
 * Require the base class.
 */
require_once 'Log/XML.php';

require_once 'XML/Beautifier.php';

/**
 * This is an abstract implementation of Log_XML.  It extends Log_XML with
 * DOM functionality.
 *
 * @category Logging
 * @package  Log
 * @author Michael Peter Gower <designatevoid@gmail.com>
 * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
 * @link     http://pear.php.net/pepr/pepr-proposal-show.php?id=487
 * @since    Log_XML 1.0
 */
abstract class Log_XML_DOM extends Log_XML
{

    public function __construct($filename = Log_XML::DEFAULT_FILENAME,
    $ident    = '',
    $conf     = array(),
    $level    = PEAR_LOG_DEBUG)
    {
        parent::__construct($filename, $ident, $conf, $level);

        if (isset($conf['schema'])) {
            $this->schema = $this->fileToolkit->getRealPath($conf['schema']);
        }
    }


    public function open()
    {
        // If opened, then nothing to do
        if ($this->_opened === true) {
            return true;
        }

            // Call the parent
        $ret = parent::open();
        if ($ret === false) {
            return false;
        }

            // Does direct file access

            // Determine whether the log file needs to be created.
        $creating = !file_exists($this->filename);


            // Init the DOMDocument.

        if (!$creating && $this->append === true && filesize($this->filename) != 0) {
            // appending

            // Create the DOMDocument
            $this->domDocument = new DOMDocument();

                // load returns true on success, false on failure
            $this->_opened = $this->domDocument->load($this->filename);

                // if we can't load, or if it isn't a valid log, exit w/ failure
            if (!$this->_opened || !$this->validateLog()) {
                return false;
            }
            $this->domDocument->formatOutput = true;

        } else {
            // create
            $this->domDocument = new DOMDocument('1.0');
            $this->_opened     = true;

                // if we can't create, return with error
            if ($this->domDocument === null) {
                $this->_opened = false;
                return false;
            }

                // if we can't create or save the header, delete and return false
            if (!$this->createHeader() || !$this->flush()) {
                if (is_file($this->filename)) {
                    unlink($this->filename);
                }
                $this->_opened = false;
                return false;
            }
            chmod($this->filename, $this->mode);
        }
        return $this->_opened;
    }

    public function close()
    {
        $ret = parent::close();
        $this->domDocument = null;
        return ($this->_opened === false && $ret !== false);
    }


    /**
     * Returns the XML of the log as a string.
     *
     * @return string It returns the XML.
     */
    public function getXML()
    {
        return $this->domDocument->saveXML();
    }


    /**
     * Alias of getXML().
     *
     * @return string It returns the XML as a string.
     */
    public function toString()
    {
        return $this->getXML();
    }


    /**
     * Returns the beautified XML.
     *
     * @return string Returns the beautified XML as a string,
     *     or null on error.
     */
    public function getFormattedString()
    {
        $beautifier = new XML_Beautifier();
        $result     = $beautifier->formatString($this->getXML());

        if (PEAR::isError($result)) {
            return null;
        }
        return $result;
    }


    /**
     * Alias of getFormattedString.  Returns the beautified XML.
     *
     * @return string Returns the beautified XML as a string.
     */
    public function getFormattedXML()
    {
        print "getFormattedXML()\n";
        return $this->getFormattedString();
    }

    public function flush()
    {
        // Does file access

        $ret = parent::flush();
        return $ret && $this->domDocument->save($this->filename);
    }


    /**
     * Returns the DOMDocument that holds the xml data.
     *
     * @return DOMDocument holds our xml data
     */
    public function getDomDocument()
    {
        return $this->domDocument;
    }

    /**
     * Creates and appends a node.
     *
     * Can have text and/or an id field.
     *
     * @param DOMNode $node_parent the node to attach the new node to
     * @param string  $string_name the name of the new node,
     *      will come to &lt;$string_name&gt;
     * @param mixed   $value       the contents of the new node
     *
     * @return DOMNode the new child node
     */
    abstract protected function appendElement($node_parent,
    $string_name,
    $value = null);


    /**
     * Returns true if the log validates against the schema.
     *
     * @return bool true on success, false on failure
     */
    protected function validateLog()
    {
        if ($this->schema === null) {
            return true;
        } else if (!is_file($this->schema)) {
            return false;
        }

        return $this->domDocument->schemaValidate($this->schema);
    }

    /**
     * Handle to the DOM Document.
     * @var DOMDocument
     */
    protected $domDocument = null;

    /**
     * A path to the xml schema to validate a logfile to append to.
     *
     * @var string
     */
    protected $schema = null;
}
